/**
 * Copyright (c) 2006-2015, JGraph Ltd
 * Copyright (c) 2006-2015, Gaudenz Alder
 */
/**
 * Class: mxPopupMenu
 *
 * Basic popup menu. To add a vertical scrollbar to a given submenu, the
 * following code can be used.
 *
 * (code)
 * var mxPopupMenuShowMenu = mxPopupMenu.prototype.showMenu;
 * mxPopupMenu.prototype.showMenu = function()
 * {
 *   mxPopupMenuShowMenu.apply(this, arguments);
 *
 *   this.div.style.overflowY = 'auto';
 *   this.div.style.overflowX = 'hidden';
 *   this.div.style.maxHeight = '160px';
 * };
 * (end)
 *
 * Constructor: mxPopupMenu
 *
 * Constructs a popupmenu.
 *
 * Event: mxEvent.SHOW
 *
 * Fires after the menu has been shown in <popup>.
 */
function mxPopupMenu (factoryMethod) {
  this.factoryMethod = factoryMethod

  if (factoryMethod != null) {
    this.init()
  }
};

/**
 * Extends mxEventSource.
 */
mxPopupMenu.prototype = new mxEventSource()
mxPopupMenu.prototype.constructor = mxPopupMenu

/**
 * Variable: submenuImage
 *
 * URL of the image to be used for the submenu icon.
 */
mxPopupMenu.prototype.submenuImage = mxClient.imageBasePath + '/submenu.gif'

/**
 * Variable: zIndex
 *
 * Specifies the zIndex for the popupmenu and its shadow. Default is 10006.
 */
mxPopupMenu.prototype.zIndex = 10006

/**
 * Variable: factoryMethod
 *
 * Function that is used to create the popup menu. The function takes the
 * current panning handler, the <mxCell> under the mouse and the mouse
 * event that triggered the call as arguments.
 */
mxPopupMenu.prototype.factoryMethod = null

/**
 * Variable: useLeftButtonForPopup
 *
 * Specifies if popupmenus should be activated by clicking the left mouse
 * button. Default is false.
 */
mxPopupMenu.prototype.useLeftButtonForPopup = false

/**
 * Variable: enabled
 *
 * Specifies if events are handled. Default is true.
 */
mxPopupMenu.prototype.enabled = true

/**
 * Variable: itemCount
 *
 * Contains the number of times <addItem> has been called for a new menu.
 */
mxPopupMenu.prototype.itemCount = 0

/**
 * Variable: autoExpand
 *
 * Specifies if submenus should be expanded on mouseover. Default is false.
 */
mxPopupMenu.prototype.autoExpand = false

/**
 * Variable: smartSeparators
 *
 * Specifies if separators should only be added if a menu item follows them.
 * Default is false.
 */
mxPopupMenu.prototype.smartSeparators = false

/**
 * Variable: labels
 *
 * Specifies if any labels should be visible. Default is true.
 */
mxPopupMenu.prototype.labels = true

/**
 * Function: init
 *
 * Initializes the shapes required for this vertex handler.
 */
mxPopupMenu.prototype.init = function () {
  // Adds the inner table
  this.table = document.createElement('table')
  this.table.className = 'mxPopupMenu'

  this.tbody = document.createElement('tbody')
  this.table.appendChild(this.tbody)

  // Adds the outer div
  this.div = document.createElement('div')
  this.div.className = 'mxPopupMenu'
  this.div.style.display = 'inline'
  this.div.style.zIndex = this.zIndex
  this.div.appendChild(this.table)

  // Disables the context menu on the outer div
  mxEvent.disableContextMenu(this.div)
}

/**
 * Function: isEnabled
 *
 * Returns true if events are handled. This implementation
 * returns <enabled>.
 */
mxPopupMenu.prototype.isEnabled = function () {
  return this.enabled
}

/**
 * Function: setEnabled
 *
 * Enables or disables event handling. This implementation
 * updates <enabled>.
 */
mxPopupMenu.prototype.setEnabled = function (enabled) {
  this.enabled = enabled
}

/**
 * Function: isPopupTrigger
 *
 * Returns true if the given event is a popupmenu trigger for the optional
 * given cell.
 *
 * Parameters:
 *
 * me - <mxMouseEvent> that represents the mouse event.
 */
mxPopupMenu.prototype.isPopupTrigger = function (me) {
  return me.isPopupTrigger() || (this.useLeftButtonForPopup && mxEvent.isLeftMouseButton(me.getEvent()))
}

/**
 * Function: addItem
 *
 * Adds the given item to the given parent item. If no parent item is specified
 * then the item is added to the top-level menu. The return value may be used
 * as the parent argument, ie. as a submenu item. The return value is the table
 * row that represents the item.
 *
 * Paramters:
 *
 * title - String that represents the title of the menu item.
 * image - Optional URL for the image icon.
 * funct - Function associated that takes a mouseup or touchend event.
 * parent - Optional item returned by <addItem>.
 * iconCls - Optional string that represents the CSS class for the image icon.
 * IconsCls is ignored if image is given.
 * enabled - Optional boolean indicating if the item is enabled. Default is true.
 * active - Optional boolean indicating if the menu should implement any event handling.
 * Default is true.
 */
mxPopupMenu.prototype.addItem = function (title, image, funct, parent, iconCls, enabled, active) {
  parent = parent || this
  this.itemCount++

  // Smart separators only added if element contains items
  if (parent.willAddSeparator) {
    if (parent.containsItems) {
      this.addSeparator(parent, true)
    }

    parent.willAddSeparator = false
  }

  parent.containsItems = true
  var tr = document.createElement('tr')
  tr.className = 'mxPopupMenuItem'
  var col1 = document.createElement('td')
  col1.className = 'mxPopupMenuIcon'

  // Adds the given image into the first column
  if (image != null) {
    var img = document.createElement('img')
    img.src = image
    col1.appendChild(img)
  } else if (iconCls != null) {
    var div = document.createElement('div')
    div.className = iconCls
    col1.appendChild(div)
  }

  tr.appendChild(col1)

  if (this.labels) {
    var col2 = document.createElement('td')
    col2.className = 'mxPopupMenuItem' +
			((enabled != null && !enabled) ? ' mxDisabled' : '')

    mxUtils.write(col2, title)
    col2.align = 'left'
    tr.appendChild(col2)

    var col3 = document.createElement('td')
    col3.className = 'mxPopupMenuItem' +
			((enabled != null && !enabled) ? ' mxDisabled' : '')
    col3.style.paddingRight = '6px'
    col3.style.textAlign = 'right'

    tr.appendChild(col3)

    if (parent.div == null) {
      this.createSubmenu(parent)
    }
  }

  parent.tbody.appendChild(tr)

  if (active != false && enabled != false) {
    var currentSelection = null

    mxEvent.addGestureListeners(tr,
      mxUtils.bind(this, function (evt) {
        this.eventReceiver = tr

        if (parent.activeRow != tr && parent.activeRow != parent) {
          if (parent.activeRow != null && parent.activeRow.div.parentNode != null) {
            this.hideSubmenu(parent)
          }

          if (tr.div != null) {
            this.showSubmenu(parent, tr)
            parent.activeRow = tr
          }
        }

        // Workaround for lost current selection in page because of focus in IE
        if (document.selection != null && (mxClient.IS_QUIRKS || document.documentMode == 8)) {
          currentSelection = document.selection.createRange()
        }

        mxEvent.consume(evt)
      }),
      mxUtils.bind(this, function (evt) {
        if (parent.activeRow != tr && parent.activeRow != parent) {
          if (parent.activeRow != null && parent.activeRow.div.parentNode != null) {
            this.hideSubmenu(parent)
          }

          if (this.autoExpand && tr.div != null) {
            this.showSubmenu(parent, tr)
            parent.activeRow = tr
          }
        }

        // Sets hover style because TR in IE doesn't have hover
        tr.className = 'mxPopupMenuItemHover'
      }),
      mxUtils.bind(this, function (evt) {
        // EventReceiver avoids clicks on a submenu item
        // which has just been shown in the mousedown
        if (this.eventReceiver == tr) {
          if (parent.activeRow != tr) {
            this.hideMenu()
          }

          // Workaround for lost current selection in page because of focus in IE
          if (currentSelection != null) {
            // Workaround for "unspecified error" in IE8 standards
            try {
              currentSelection.select()
            } catch (e) {
              // ignore
            }

            currentSelection = null
          }

          if (funct != null) {
            funct(evt)
          }
        }

        this.eventReceiver = null
        mxEvent.consume(evt)
      })
    )

    // Resets hover style because TR in IE doesn't have hover
    mxEvent.addListener(tr, 'mouseout',
      mxUtils.bind(this, function (evt) {
        tr.className = 'mxPopupMenuItem'
      })
    )
  }

  return tr
}

/**
 * Adds a checkmark to the given menuitem.
 */
mxPopupMenu.prototype.addCheckmark = function (item, img) {
  var td = item.firstChild.nextSibling
  td.style.backgroundImage = 'url(\'' + img + '\')'
  td.style.backgroundRepeat = 'no-repeat'
  td.style.backgroundPosition = '2px 50%'
}

/**
 * Function: createSubmenu
 *
 * Creates the nodes required to add submenu items inside the given parent
 * item. This is called in <addItem> if a parent item is used for the first
 * time. This adds various DOM nodes and a <submenuImage> to the parent.
 *
 * Parameters:
 *
 * parent - An item returned by <addItem>.
 */
mxPopupMenu.prototype.createSubmenu = function (parent) {
  parent.table = document.createElement('table')
  parent.table.className = 'mxPopupMenu'

  parent.tbody = document.createElement('tbody')
  parent.table.appendChild(parent.tbody)

  parent.div = document.createElement('div')
  parent.div.className = 'mxPopupMenu'

  parent.div.style.position = 'absolute'
  parent.div.style.display = 'inline'
  parent.div.style.zIndex = this.zIndex

  parent.div.appendChild(parent.table)

  var img = document.createElement('img')
  img.setAttribute('src', this.submenuImage)

  // Last column of the submenu item in the parent menu
  td = parent.firstChild.nextSibling.nextSibling
  td.appendChild(img)
}

/**
 * Function: showSubmenu
 *
 * Shows the submenu inside the given parent row.
 */
mxPopupMenu.prototype.showSubmenu = function (parent, row) {
  if (row.div != null) {
    row.div.style.left = (parent.div.offsetLeft +
			row.offsetLeft + row.offsetWidth - 1) + 'px'
    row.div.style.top = (parent.div.offsetTop + row.offsetTop) + 'px'
    document.body.appendChild(row.div)

    // Moves the submenu to the left side if there is no space
    var left = parseInt(row.div.offsetLeft)
    var width = parseInt(row.div.offsetWidth)
    var offset = mxUtils.getDocumentScrollOrigin(document)

    var b = document.body
    var d = document.documentElement

    var right = offset.x + (b.clientWidth || d.clientWidth)

    if (left + width > right) {
      row.div.style.left = Math.max(0, (parent.div.offsetLeft - width + ((mxClient.IS_IE) ? 6 : -6))) + 'px'
    }

    mxUtils.fit(row.div)
  }
}

/**
 * Function: addSeparator
 *
 * Adds a horizontal separator in the given parent item or the top-level menu
 * if no parent is specified.
 *
 * Parameters:
 *
 * parent - Optional item returned by <addItem>.
 * force - Optional boolean to ignore <smartSeparators>. Default is false.
 */
mxPopupMenu.prototype.addSeparator = function (parent, force) {
  parent = parent || this

  if (this.smartSeparators && !force) {
    parent.willAddSeparator = true
  } else if (parent.tbody != null) {
    parent.willAddSeparator = false
    var tr = document.createElement('tr')

    var col1 = document.createElement('td')
    col1.className = 'mxPopupMenuIcon'
    col1.style.padding = '0 0 0 0px'

    tr.appendChild(col1)

    var col2 = document.createElement('td')
    col2.style.padding = '0 0 0 0px'
    col2.setAttribute('colSpan', '2')

    var hr = document.createElement('hr')
    hr.setAttribute('size', '1')
    col2.appendChild(hr)

    tr.appendChild(col2)

    parent.tbody.appendChild(tr)
  }
}

/**
 * Function: popup
 *
 * Shows the popup menu for the given event and cell.
 *
 * Example:
 *
 * (code)
 * graph.panningHandler.popup = function(x, y, cell, evt)
 * {
 *   mxUtils.alert('Hello, World!');
 * }
 * (end)
 */
mxPopupMenu.prototype.popup = function (x, y, cell, evt) {
  if (this.div != null && this.tbody != null && this.factoryMethod != null) {
    this.div.style.left = x + 'px'
    this.div.style.top = y + 'px'

    // Removes all child nodes from the existing menu
    while (this.tbody.firstChild != null) {
      mxEvent.release(this.tbody.firstChild)
      this.tbody.removeChild(this.tbody.firstChild)
    }

    this.itemCount = 0
    this.factoryMethod(this, cell, evt)

    if (this.itemCount > 0) {
      this.showMenu()
      this.fireEvent(new mxEventObject(mxEvent.SHOW))
    }
  }
}

/**
 * Function: isMenuShowing
 *
 * Returns true if the menu is showing.
 */
mxPopupMenu.prototype.isMenuShowing = function () {
  return this.div != null && this.div.parentNode == document.body
}

/**
 * Function: showMenu
 *
 * Shows the menu.
 */
mxPopupMenu.prototype.showMenu = function () {
  // Disables filter-based shadow in IE9 standards mode
  if (document.documentMode >= 9) {
    this.div.style.filter = 'none'
  }

  // Fits the div inside the viewport
  document.body.appendChild(this.div)
  mxUtils.fit(this.div)
}

/**
 * Function: hideMenu
 *
 * Removes the menu and all submenus.
 */
mxPopupMenu.prototype.hideMenu = function () {
  if (this.div != null) {
    if (this.div.parentNode != null) {
      this.div.parentNode.removeChild(this.div)
    }

    this.hideSubmenu(this)
    this.containsItems = false
    this.fireEvent(new mxEventObject(mxEvent.HIDE))
  }
}

/**
 * Function: hideSubmenu
 *
 * Removes all submenus inside the given parent.
 *
 * Parameters:
 *
 * parent - An item returned by <addItem>.
 */
mxPopupMenu.prototype.hideSubmenu = function (parent) {
  if (parent.activeRow != null) {
    this.hideSubmenu(parent.activeRow)

    if (parent.activeRow.div.parentNode != null) {
      parent.activeRow.div.parentNode.removeChild(parent.activeRow.div)
    }

    parent.activeRow = null
  }
}

/**
 * Function: destroy
 *
 * Destroys the handler and all its resources and DOM nodes.
 */
mxPopupMenu.prototype.destroy = function () {
  if (this.div != null) {
    mxEvent.release(this.div)

    if (this.div.parentNode != null) {
      this.div.parentNode.removeChild(this.div)
    }

    this.div = null
  }
}
